package compiler;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import nodes.AbstractNode;
import nodes.ArrayNode;
import nodes.AssignmentNode;
import nodes.ConstNode;
import nodes.ContNode;
import nodes.DeclarationsNode;
import nodes.ExpressionNode;
import nodes.FPSectionNode;
import nodes.FieldListNode;
import nodes.IdentNode;
import nodes.IfNode;
import nodes.IntegerNode;
import nodes.ListNode;
import nodes.ModuleNode;
import nodes.NegationNode;
import nodes.OperatorNode;
import nodes.PrintNode;
import nodes.ProcedureBodyNode;
import nodes.ProcedureCallNode;
import nodes.ProcedureDeclarationNode;
import nodes.ProcedureHeadingNode;
import nodes.RecordNode;
import nodes.RepeatNode;
import nodes.StatementNode;
import nodes.StringNode;
import nodes.TypeNode;
import nodes.VarNode;
import nodes.WhileNode;


public class Parser {
	
	private final List<Yytoken> tokenList;
	private final Iterator<Yytoken> tokenIterator;
	private Yytoken nextToken;
	
	public Parser(List<Yytoken> tokenList){
		this.tokenList=tokenList;
		this.tokenIterator=this.tokenList.iterator();
	}
	
	public AbstractNode parse(){
		insymbol();
		return module();
	}


	static String spaces = "";

	/*
	 * Hilfsroutinen
	 */

	public void indent() {
		spaces = spaces + "  ";
	}

	public void unindent() {
		spaces = spaces.substring(2);
	}

	public void compile(String str) {
		System.out.println(spaces + " -- " + str);
	}

	public void outStr(String str) {
		System.out.println(spaces + " -- " + str + " ");
	}

	public void outInt(int i) {
		System.out.println(spaces + " -- " + i + " ");
	}

	public void outOp(String op) {
		System.out.println(spaces + " -- " + op);
	}

	public void error(String str) {
		System.out.println("==> Error: " + str);
		System.exit(0);
	}
	public void errorExp(String str,Yytoken token) {
		System.out.println("==> Error: found "+token.value()+" expected " +str +" in "+token.line()+", "+token.column());
		System.exit(0);
	}

	public void errorExp(String str) {
		errorExp(str,nextToken);
	}
	
	public void insymbol() {
		nextToken=tokenIterator.next();
	}

	/*
	 * Parserroutinen
	 */

	/******************* Ausdrücke ******************/
	
	private ModuleNode module(){
		IdentNode ident=null;
		DeclarationsNode declarations=null;
		ListNode<StatementNode> statementSeq=null;
		int line=-1;
		int column=-1;
		if(nextToken.value().equalsIgnoreCase("MODULE")){
			outStr("MODULE");
			line=nextToken.line();
			column=nextToken.column();
			insymbol();
			if(nextToken.isIdent()){
				outStr(nextToken.value());
				ident=new IdentNode(nextToken);
				insymbol();
				if(nextToken.value().equals(";")){
					outStr(";");
					insymbol();
					declarations=declarations();
					if(nextToken.isBegin()){
						outStr("BEGIN MODULE");
						insymbol();
						statementSeq=statementSequence();
					}
					if(nextToken.isEnd()){
						outStr("END MODULE");
						insymbol();
						if(nextToken.isIdent()){
							outStr(nextToken.value());
							insymbol();
							if(nextToken.value().equals(".")){
								outStr(".");
								insymbol();
							}
							else{
								errorExp(".");
							}
						}
						else{
							errorExp("Ident");
						}
					}
					else{
						errorExp("END");
					}
				}
				else{
					errorExp(";");
				}
			}
			else{
				errorExp("Ident");
			}
		}
		else{
			errorExp("MODULE");
		}
		return new ModuleNode(ident,declarations,statementSeq,line,column);
	}
	
	private ListNode<ExpressionNode> actualParameters(){
		List<ExpressionNode> parameters=new LinkedList<ExpressionNode>();
		int line=nextToken.line();
		int column=nextToken.column();
		if(nextToken.value().equals("(")){
			outStr("(");
			insymbol();
			if(nextToken.value().equals(")")){
				outStr(")");
				insymbol();
			}
			else{
			    ExpressionNode exprNode = expression();
			    if (exprNode instanceof ContNode) {
			        exprNode = ((ContNode)exprNode).node();
			    }
				parameters.add(exprNode);
				while(nextToken.value().equals(",")){
					outStr(",");
					insymbol();
					exprNode = expression();
					if (exprNode instanceof ContNode) {
	                    exprNode = ((ContNode)exprNode).node();
	                }
					parameters.add(exprNode);
				}
				if(nextToken.value().equals(")")){
					outStr(")");
					insymbol();
				}
				else{
					errorExp(")");
				}
			}
		}
		else {
			errorExp("(");
		}
		return new ListNode<ExpressionNode>(parameters,line,column);
	}
	
	private ArrayNode arrayType(){
		int line=nextToken.line();
		int column=nextToken.column();
		AbstractNode indexExpression=null,type=null;
		if(nextToken.value().equalsIgnoreCase("ARRAY")){
			outStr("ARRAY");
			insymbol();
			if(nextToken.value().equals("[")){
				outStr("[");
				insymbol();
				indexExpression=indexExpression();
				if(nextToken.value().equals("]")){
					outStr("]");
					insymbol();
					if(nextToken.value().equalsIgnoreCase("OF")){
						outStr("OF");
						insymbol();
						type=type();
					}
					else errorExp("OF");
				}
				else errorExp("]");
			}
			else errorExp("[");
		}
		else errorExp("ARRAY");
		return new ArrayNode(indexExpression,type,line,column);
	}
	
	private AssignmentNode assignment(Yytoken ident){
		OperatorNode selector=selector(ident);
		ExpressionNode expression=null;
		int line=0;
		int column=0;
		if(nextToken.value().equals(":=")){
			outStr(":=");
			line=nextToken.line();
			column=nextToken.column();
			insymbol();
			expression=expression();
		} else{
			errorExp(":=");
		}
		if(selector!=null) return new AssignmentNode(null,selector,expression,line,column);
		else return new AssignmentNode(new IdentNode(ident),null,expression,line,column);
	}
	
	private IdentNode constIdent(){
		IdentNode node=null;
		if(nextToken.isIdent()){
			outStr(nextToken.value());
			node=new IdentNode(nextToken);
			insymbol();
		}
		else errorExp("Ident");
		return node;
	}
	
	private DeclarationsNode declarations(){
		int line=nextToken.line();
		int column=nextToken.column();
		List<ConstNode> constNodes = new LinkedList<ConstNode>();
		List<VarNode> varNodes = new LinkedList<VarNode>();
		List<TypeNode> typeNodes = new LinkedList<TypeNode>();
		List<ProcedureDeclarationNode> procDeclNodes = new LinkedList<ProcedureDeclarationNode>();
		if(nextToken.value().equalsIgnoreCase("CONST")){
			IdentNode constIdent;
			ExpressionNode constExpression;
			outStr("CONST");
			insymbol();
			if(nextToken.isIdent()){
				outStr(nextToken.value());
				constIdent=new IdentNode(nextToken);
				int constLine=nextToken.line();
				int constColumn=nextToken.column();
				insymbol();
				if(nextToken.value().equals("=")){
					outStr("=");
					insymbol();
					constExpression=expression();
					constNodes.add(new ConstNode(constIdent,constExpression,constLine,constColumn));
					if(nextToken.value().equals(";")){
						outStr(";");
						insymbol();
						while(nextToken.isIdent()){
							outStr(nextToken.value());
							constIdent=new IdentNode(nextToken);
							constLine=nextToken.line();
							constColumn=nextToken.column();
							insymbol();
							if(nextToken.value().equals("=")){
								outStr("=");
								insymbol();
								constExpression=expression();
								constNodes.add(new ConstNode(constIdent,constExpression,constLine,constColumn));
								if(nextToken.value().equals(";")){
									outStr(";");
									insymbol();
								}
								else errorExp(";");
							}
							else errorExp("=");
						}
					}
					else errorExp(";");
				}
				else errorExp("=");
			}
			else errorExp("Ident");
		}
		if(nextToken.value().equalsIgnoreCase("TYPE")){
			outStr("TYPE");
			IdentNode typeIdent;
			AbstractNode typeType;
			insymbol();
			if(nextToken.isIdent()){
				outStr(nextToken.value());
				int typeLine=nextToken.line();
				int typeColumn=nextToken.column();
				typeIdent=new IdentNode(nextToken);
				insymbol();
				if(nextToken.value().equals("=")){
					outStr("=");
					insymbol();
					typeType=type();
					typeNodes.add(new TypeNode(typeIdent,typeType,typeLine,typeColumn));
					if(nextToken.value().equals(";")){
						outStr(";");
						insymbol();
						while(nextToken.isIdent()){
							outStr(nextToken.value());
							typeLine=nextToken.line();
							typeColumn=nextToken.column();
							typeIdent=new IdentNode(nextToken);
							insymbol();
							if(nextToken.value().equals("=")){
								outStr("=");
								insymbol();
								typeType=type();
								typeNodes.add(new TypeNode(typeIdent,typeType,typeLine,typeColumn));
								if(nextToken.value().equals(";")){
									outStr(";");
									insymbol();
								}
								else errorExp(";");
							}
							else errorExp("=");
						}
					}
					else errorExp(";");
				}
				else errorExp("=");
			}
			else errorExp("Ident");			
		}
		if(nextToken.value().equalsIgnoreCase("VAR")){
			outStr("VAR");
			ListNode<IdentNode> varIdentList;
			AbstractNode varType;
			insymbol();
			if(nextToken.isIdent()){
				int varLine=nextToken.line();
				int varColumn=nextToken.column();
				varIdentList=identList();
				if(nextToken.value().equals(":")){
					outStr(":");
					insymbol();
					varType=type();
					varNodes.add(new VarNode(varIdentList,varType,varLine,varColumn));
					if(nextToken.value().equals(";")){
						outStr(";");
						insymbol();
						while(nextToken.isIdent()){
							varLine=nextToken.line();
							varColumn=nextToken.column();
							varIdentList=identList();
							if(nextToken.value().equals(":")){
								outStr(":");
								insymbol();
								varType=type();
								varNodes.add(new VarNode(varIdentList,varType,varLine,varColumn));
								if(nextToken.value().equals(";")){
									outStr(";");
									insymbol();
								}
								else errorExp(";");
							}
							else errorExp(":");
						}
					}
					else errorExp(";");
				}
				else errorExp(":");
			}
			else errorExp("IdentList");			
		}
		while(nextToken.value().equalsIgnoreCase("PROCEDURE")){
			procDeclNodes.add(procedureDeclaration());
			if(nextToken.value().equals(";")){
				outStr(";");
				insymbol();
			}
			else errorExp(";");
		}
		return new DeclarationsNode(constNodes,typeNodes,varNodes,procDeclNodes,line,column);
	}
	
	private ExpressionNode expression(){
		int line=nextToken.line();
		int column=nextToken.column();
		ExpressionNode left=simpleExpression();
		List<String> operators=Arrays.asList("=","#", "<","<=",">",">=");
		if(operators.contains(nextToken.value())){
			outStr(nextToken.value());
			String operator=nextToken.value();
			insymbol();
			return new OperatorNode(operator,left,simpleExpression(),line,column);
		}
		else return left;
	}
	
	private ExpressionNode factor(){
		ExpressionNode node=null;
		int line=nextToken.line();
		int column=nextToken.column();
		if(nextToken.isIdent()){
			outStr(nextToken.value());
			Yytoken ident=nextToken;
			insymbol();
			OperatorNode selectorNode=selector(ident);
			if(selectorNode==null) node=new ContNode(new IdentNode(ident),line,column);
			else node=new ContNode(selectorNode,line,column);
		}
		else if(nextToken.isInteger()){
			outStr(nextToken.value());
			node=new IntegerNode(nextToken);
			insymbol();
		}
		else if(nextToken.isString()){
			outStr(nextToken.value());
			node=new StringNode(nextToken);
			insymbol();
		}
		else if(nextToken.value().equalsIgnoreCase("READ")){
			node=read();
		}
		else if(nextToken.value().equals("(")){
			outStr("(");
			insymbol();
			node=expression();
			if(nextToken.value().equals(")")){
				outStr(")");
				insymbol();
			}
			else{
				errorExp(")");
			}
		}
		else{
			errorExp("(, Integer, Ident, READ or string");
		}
		return node;
	}
	
	private FieldListNode fieldList(){
		int line=nextToken.line();
		int column=nextToken.column();
		ListNode<IdentNode> identList=null;
		AbstractNode type=null;
		if(nextToken.isIdent()){
			identList=identList();
			if(nextToken.value().equals(":")){
				outStr(":");
				insymbol();
				type=type();
				return new FieldListNode(identList,type,line,column);
			}
			else errorExp(":");
		}
		return null;
	}
	
	private ListNode<FPSectionNode>formalParameters(){
		int line=nextToken.line();
		int column=nextToken.column();
		List<FPSectionNode> list=new LinkedList<FPSectionNode>(); 
		list.add(fpSection());
		while(nextToken.value().equals(";")){
			outStr(";");
			insymbol();
			list.add(fpSection());
		}
		return new ListNode<FPSectionNode>(list,line,column);
	}
	
	private FPSectionNode fpSection(){
		int line=nextToken.line();
		int column=nextToken.column();
		boolean isVar=false;
		AbstractNode type=null;
		if(nextToken.value().equalsIgnoreCase("VAR")){
			outStr("VAR");
			isVar=true;
			insymbol();
		}
		ListNode<IdentNode> identList=identList();
		if(nextToken.value().equals(":")){
			outStr(":");
			insymbol();
			type=type();
		}
		else errorExp(":");
		return new FPSectionNode(isVar,identList,type,line,column);
	}
	
	private ListNode<IdentNode> identList(){
		int line=nextToken.line();
		int column=nextToken.column();
		List<IdentNode> list=new LinkedList<IdentNode>();
		if(nextToken.isIdent()){
			outStr(nextToken.value());
			list.add(new IdentNode(nextToken));
			insymbol();
			while(nextToken.value().equals(",")){
				outStr(",");
				insymbol();
				if(nextToken.isIdent()){
					outStr(nextToken.value());
					list.add(new IdentNode(nextToken));
					insymbol();
				}
				else errorExp("Ident");
			}
		}
		else errorExp("Ident");
		return new ListNode<IdentNode>(list,line,column);
	}
	
	
	private IfNode elsifStatement(){
		int line=nextToken.line();
		int column=nextToken.column();
		ExpressionNode cond=null;
		ListNode<StatementNode> thenPart=null;
		AbstractNode elsePart=null;
		if(nextToken.value().equalsIgnoreCase("ELSIF")){
			outStr("ELSIF");
			insymbol();
			cond=expression();
			if(nextToken.value().equalsIgnoreCase("THEN")){
				outStr("THEN");
				insymbol();
				thenPart=statementSequence();
				if(nextToken.value().equalsIgnoreCase("ELSIF")){
					elsePart=elsifStatement();
				}
				else if(nextToken.value().equalsIgnoreCase("ELSE")){
					outStr("ELSE");
					insymbol();
					elsePart=statementSequence();
				}
			}
			else{
				errorExp("THEN");	
			}
		}
		else {
			errorExp("ELSIF");
		}
		return new IfNode(cond,thenPart,elsePart,line,column);
	}
	
	private IfNode ifStatement(){
		int line=nextToken.line();
		int column=nextToken.column();
		ExpressionNode cond=null;
		ListNode<StatementNode> thenPart=null;
		AbstractNode elsePart=null;
		if(nextToken.value().equalsIgnoreCase("IF")){
			outStr("IF");
			insymbol();
			cond=expression();
			if(nextToken.value().equalsIgnoreCase("THEN")){
				outStr("THEN");
				insymbol();
				thenPart=statementSequence();
				if(nextToken.value().equalsIgnoreCase("ELSIF")){
					elsePart=elsifStatement();
				}
				else if(nextToken.value().equalsIgnoreCase("ELSE")){
					outStr("ELSE");
					insymbol();
					elsePart=statementSequence();
				}
				if(nextToken.value().equalsIgnoreCase("END")){
					outStr("END");
					insymbol();
				}
				else {
					errorExp("END");	
				}
			}
			else{
				errorExp("THEN");	
			}
		}
		
		else{
			errorExp("IF");
		}
		return new IfNode(cond,thenPart,elsePart,line,column);
	}
	
	
	private AbstractNode indexExpression(){
		AbstractNode result=null;
		if(nextToken.isInteger()){
			outStr(nextToken.value());
			result=new IntegerNode(nextToken);
			insymbol();
		}
		else if(nextToken.isIdent()){
			result= constIdent();
		}
		else errorExp("ConstIdent or Integer");
		return result;
	}
	
	private ProcedureBodyNode procedureBody(){
		int line=nextToken.line();
		int column=nextToken.column();
		DeclarationsNode declarations=declarations();
		ListNode<StatementNode> statementSeq=null;
		if(nextToken.isBegin()){
			outStr("BEGIN");
			insymbol();
			statementSeq=statementSequence();
			if(nextToken.isEnd()){
				outStr("END");
				insymbol();
			}
			else errorExp("END");
		}
		else errorExp("BEGIN");
		return new ProcedureBodyNode(declarations,statementSeq,line,column);
	}
	
	private ProcedureCallNode procedureCall(Yytoken ident){
		return new ProcedureCallNode(new IdentNode(ident),actualParameters(),ident.line(),ident.column());
	}
	
	private ProcedureDeclarationNode procedureDeclaration(){
		int line=nextToken.line();
		int column=nextToken.column();
		ProcedureHeadingNode procedureHeading=procedureHeading();
		ProcedureBodyNode procedureBody=null;
		if(nextToken.value().equals(";")){
			outStr(";");
			insymbol();
			procedureBody=procedureBody();
			if(nextToken.isIdent()){
				insymbol();
			}
			else errorExp("Ident");
		}
		else errorExp(";");
		return new ProcedureDeclarationNode(procedureHeading,procedureBody,line,column);
	}
	
	private ProcedureHeadingNode procedureHeading(){
		int line=nextToken.line();
		int column=nextToken.column();
		IdentNode ident=null;
		ListNode<FPSectionNode> formalParameters=null;
		if(nextToken.value().equalsIgnoreCase("PROCEDURE")){
			outStr("PROCEDURE");
			insymbol();
			if(nextToken.isIdent()){
				outStr(nextToken.value());
				ident=new IdentNode(nextToken);
				insymbol();
				if(nextToken.value().equals("(")){
					outStr("(");
					insymbol();
					if(nextToken.value().equals(")")){
						outStr(")");
						insymbol();
					}
					else{
						formalParameters=formalParameters();
						if(nextToken.value().equals(")")){
							outStr(")");
							insymbol();
						}
						else errorExp(")");
					}
				}
				else errorExp("(");
			}
			else errorExp("Ident");
		}
		else errorExp("PROCEDURE");
		return new ProcedureHeadingNode(ident,formalParameters,line,column);
	}
	
	private StringNode read(){
		StringNode node=null;
		if(nextToken.value().equalsIgnoreCase("READ")){
			outStr("READ");
			insymbol();
			//Prompt
			if(nextToken.isString()){
				outStr(nextToken.value());
				node=new StringNode(nextToken);
			}
		}
		else {
			errorExp("READ");
		}
		return node;
	}
	
	private RecordNode recordType(){
		int line=nextToken.line();
		int column=nextToken.column();
		List<FieldListNode> list=new LinkedList<FieldListNode>();
		if(nextToken.value().equalsIgnoreCase("RECORD")){
			insymbol();
			FieldListNode fieldList=fieldList();
			if(fieldList!=null) 	list.add(fieldList);
			while(nextToken.value().equalsIgnoreCase(";")){
				outStr(";");
				insymbol();
				fieldList=fieldList();
				if(fieldList!=null) 	list.add(fieldList);
			}
			if(nextToken.value().equalsIgnoreCase("END")){
				outStr("END");
				insymbol();
			}
			else errorExp("END");
		}
		else errorExp("RECORD");
		return new RecordNode(new ListNode<FieldListNode>(list,line,column),line,column);
	}

	private RepeatNode repeatStatement(){
		ListNode<StatementNode> statementSequence=null;
		ExpressionNode expression=null;
		int line=0;
		int column=0;
		if(nextToken.value().equalsIgnoreCase("REPEAT")){
			outStr("REPEAT");
			line=nextToken.line();
			column=nextToken.column();
			insymbol();
			statementSequence=statementSequence();
			if(nextToken.value().equalsIgnoreCase("UNTIL")){
				outStr("UNTIL");
				insymbol();
				expression=expression();
			} else{
				errorExp("UNTIL");
			}
		}
		else{
			errorExp("REPEAT");
		}
		return new RepeatNode(statementSequence,expression,line,column);
	}
	
	private OperatorNode selector(Yytoken ident){
		OperatorNode node=null;
		while(nextToken.value().equals(".") || nextToken.value().equals("[")){
			int line =nextToken.line();
			int column=nextToken.column();
			if(nextToken.value().equals(".")){
				outStr(".");
				insymbol();
				if(nextToken.isIdent()){
					outStr(nextToken.value());
					if(node==null) node=new OperatorNode(".",new IdentNode(ident),new IdentNode(nextToken),line,column);
					else node=new OperatorNode(".",node,new IdentNode(nextToken),line,column);
					insymbol();
				}
				else{
					errorExp("Ident");
				}
			}
			else if(nextToken.value().equals("[")){
				outStr("[");
				insymbol();
				if(node==null) node=new OperatorNode("[",new IdentNode(ident),expression(),line,column);
				else node=new OperatorNode("[",node,expression(),line,column);
				if(nextToken.value().equals("]")){
					outStr("]");
					insymbol();
				}
				else{
					errorExp("]");
				}
			}
		}
		return node;
	}
	
	private ExpressionNode simpleExpression(){
		int line=nextToken.line();
		int column=nextToken.column();
		ExpressionNode left=null,node=null;
		if(nextToken.value().equals("-")){
			outStr("-");
			insymbol();
			left=new NegationNode(term(),line,column);
		}
		else left=term();
		while(nextToken.value().equals("+") || nextToken.value().equals("-")){
			if(nextToken.value().equalsIgnoreCase("OR")) outStr("OR");
			else outStr(nextToken.value());
			String operator=nextToken.value();
			line=nextToken.line();
			column=nextToken.column();
			insymbol();
			if(node==null) node= new OperatorNode(operator,left,term(),line,column);
			else node=new OperatorNode(operator,node,term(),line,column);
		}
		if(node==null) return left;
		else return node;
	}
	
	private ListNode<StatementNode> statementSequence(){
		int line=nextToken.line();
		int column=nextToken.column();
		List<StatementNode> statementList=new LinkedList<StatementNode>();
		StatementNode statementNode=statement();
		if(statementNode!=null) statementList.add(statementNode);
		while(nextToken.value().equals(";")){
			outStr(";");
			insymbol();
			statementNode=statement();
			if(statementNode!=null) statementList.add(statementNode);
		}
		return new ListNode<StatementNode>(statementList,line,column);
	}
	
	private StatementNode statement(){
		// Assignment and ProcedureCall
		if(nextToken.isIdent()){
			Yytoken ident=nextToken;
			outStr(nextToken.value());
			insymbol();
			if(nextToken.value().equals("(")){
				return procedureCall(ident);
			} else{
				return assignment(ident);
			}
		}
		// IfStatement
		else if(nextToken.value().equalsIgnoreCase("IF")){
			return ifStatement();
		}
		// WhileStatement
		else if(nextToken.value().equalsIgnoreCase("WHILE")){
			return whileStatement();
		}
		// RepeatStatement
		else if(nextToken.value().equalsIgnoreCase("REPEAT")){
			return repeatStatement();
		}
		else if(nextToken.value().equalsIgnoreCase("PRINT")){
			outStr("PRINT");
			int line=nextToken.line();
			int column=nextToken.column();
			insymbol();
			ExpressionNode expressionNode=expression();
			return new PrintNode(expressionNode,line,column);
		}
		return null;
	}
	
	private ExpressionNode term(){
		ExpressionNode left=null,node=null;
		int line=nextToken.line();
		int column=nextToken.column();
		left=factor();
		while(nextToken.value().equals("*") || nextToken.value().equals("/")){
			outStr(nextToken.value());
			String operator=nextToken.value();
			line=nextToken.line();
			column=nextToken.column();
			insymbol();
			if(node==null) node=new OperatorNode(operator,left,factor(),line,column);
			else node=new OperatorNode(operator,node,factor(),line,column);
		}
		if(node==null) return left;
		else return node;
	}
	
	private AbstractNode type(){
		AbstractNode type=null;
		if(nextToken.isIdent()){
			outStr(nextToken.value());
			type=new IdentNode(nextToken);
			insymbol();
		}
		else if(nextToken.value().equalsIgnoreCase("ARRAY")){
			type=arrayType();
		}
		else if(nextToken.value().equalsIgnoreCase("RECORD")){
			type=recordType();
		}
		else errorExp("Ident, ArrayType or RecordType");
		return type;
	}
	
	private WhileNode whileStatement(){
		ExpressionNode expression=null;
		ListNode<StatementNode> statementSeq=null;
		int line=0;
		int column=0;
		if(nextToken.value().equalsIgnoreCase("WHILE")){
			outStr("WHILE");
			line=nextToken.line();
			column=nextToken.column();
			insymbol();
			expression=expression();
			if(nextToken.value().equalsIgnoreCase("DO")){
				outStr("DO");
				insymbol();
				statementSeq=statementSequence();
				if(nextToken.value().equalsIgnoreCase("END")){
					outStr("END");
					insymbol();
				}
				else {
					errorExp("END");
				}
			}
			else{
				errorExp("DO");
			}
		}
		else{
			errorExp("WHILE");
		}
		return new WhileNode(expression,statementSeq,line,column);
	}


	/*
	 * main
	 */

	public static void main(String[] argv) {
		List<Yytoken> list=new LinkedList<Yytoken>();
		if (argv.length == 0) {
			System.out.println("Usage : java Parser <inputfile>");
		} else {
			for (int i = 0; i < argv.length; i++) {
				try {
					String infile = argv[i];
					Scanner scanner = new Scanner(new java.io.FileReader(infile));
					Yytoken token=scanner.yylex();
					while(token!=null){
						if(token!=null){
							list.add(token);
						}
						token=scanner.yylex();
					}
					list.add(new Yytoken("Last","Last",-1,-1));
					//System.out.println(list);

				} catch (java.io.FileNotFoundException e) {
					System.out.println("File not found : \"" + argv[i] + "\"");
				} catch (Exception e) {
					System.out.println("Unexpected exception:");
					e.printStackTrace();
				}
			}
			System.out.println("\n===============");
			System.out.println("Starting Parser");
			System.out.println("===============\n");
			Parser parser=new Parser(list);
			AbstractNode module=parser.parse();
			System.out.println("\n===============");
			System.out.println("AST");
			System.out.println("===============\n");
			module.print();
		}
	}

}